AE = typeof AE !== 'undefined' ? AE : {};
AE.Expanders = (function() {
    var Helper = AE.Helper;

    var ResponseParser = {
        "HTML" : function(html, selector, shortUrl, xhr) {
            var div = document.createElement("DIV");
            div.innerHTML = html;
            var el = div.querySelectorAll(selector)[0],
                realUrl = "";

            if (el) {
                switch (el.nodeName) {
                    case "A":
                        realUrl = el.href;
                        break;
                    case "INPUT":
                        realUrl = el.value;
                        break;
                    default:
                        realUrl = el.innerHTML || "";
                        break;
                }
            }

            return realUrl;
        },

        "JSON" : function(jsonStr, field, shortUrl, xhr) {
            var json = JSON.parse(jsonStr),
                parts = (field + "").split("."),
                part,
                realUrl = "",
                obj = json;

            while (obj && (part = parts.shift())) {
                obj = obj[part];
            }

            return obj;
        }
    };

    var DefaultExpander = {
        /**
         * Request method, POST or GET, default is POST
         * @type    String
         */
        method : "POST",

        /**
         * The expander' api url
         * @type    String
         */
        apiUrl : "",

        /**
         * Extra headers which needs send to server
         * @type    Object<Stinrg:String>
         */
        headers : {
            "X-Requested-With" : "XMLHttpRequest"
        },

        responseType : "",

        responseFilter : "",

        responseStatus : 200,

        callback : function(xhr, userCallback, shortUrl) {
            var status = this.responseStatus,
                allowStatus = status instanceof Array ? status : [status];

            if (xhr.readyState === 4 && allowStatus.indexOf(xhr.status) > -1) {
                var parser = ResponseParser[(this.responseType || "NONE").toUpperCase()];
                if (parser) {
                    var filter = Helper.format(this.responseFilter, {
                        shortUrl : shortUrl
                    });
                    var realUrl;
                    try {
                        realUrl = parser(xhr.responseText, this.responseFilter, shortUrl, xhr);
                    } catch (e) {
                        //alert(e);
                    }
                    if (realUrl) {
                        userCallback(realUrl, shortUrl);
                    }
                }
            }
        },

        /**
         * Get request params, default format this.requestParmas with short url
         * @param   {String}    shortUrl    Short url
         */
        getData : function(shortUrl) { 
            if (this.requestData) {
                return Helper.format(this.requestData, {
                    shortUrl : encodeURIComponent(shortUrl)
                });
            }

            return "";
        },

        /**
         * Get request data, default format this.requestParmas with short url
         * @param   {String}    shortUrl    Short url
         */
        getParams : function(shortUrl) {
            if (this.requestParams) {
                return Helper.format(this.requestParams, {
                    shortUrl : encodeURIComponent(shortUrl)
                });
            }

            return "";
        },

        /**
         * Get original url of the short url
         * @param   {String}    shortUrl    The short url
         * @param   {String}    callback    When got the real url, send it to the callback
         */
        getOriginalUrl : function(shortUrl, callback) {
            var data = this.getData(shortUrl),
                params = this.getParams(shortUrl);

            var cb = (function(self, cb, ucb, surl) {
                return function(xhr) { cb.call(self, xhr, ucb, surl); };
            })(this, this.callback, callback, shortUrl);

            return Helper.makeRequest({
                url : this.apiUrl,
                method : this.method,
                headers : this.headers,
                data : data,
                params : params,
                callback : cb
            });
        }
    };

    var Expanders = {
        /**
         * Expanders stroe
         * @type    Object
         */
        store : {},

        /**
         * Expanders list
         * @type    Array<String>
         */
        expanders : [],

        /**
         * Add an expander
         * @param   {String}    name
         * @param   {Object}    obj
         */
        add : function(obj) {
            if (obj instanceof Array) {
                obj.forEach(function(o) {
                    Expanders.add(o);
                });
            } else if (obj instanceof Object) {
                if (!obj || !obj.apiUrl) {
                    return;
                }
                var elA = document.createElement("A"), name;
                elA.href = obj.apiUrl;
                name = elA.hostname || obj.apiUrl;
                this.store[name] = Helper.mergeObject(DefaultExpander, obj);
                this.expanders.push(name);
            }
        },

        getExpander : function(name) {
            return this.store[name] || null;
        },

        getExpanders : function() {
            return this.expanders;
        },


        /**
         * Get real url with the specified expander
         * @param   {String}    name
         * @param   {String}    url
         * @param   {Function}  callback
         */
        use : function(name, url, callback) {
            return (this.store[name] || this.store['untr.im']).getOriginalUrl(url, callback);
        }
    };

    return Expanders;
})();
